﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Windows.Forms;
using MathNet.Numerics;
using MathNet.Numerics.LinearAlgebra;
using MentalAlchemy.Atomics;
using MentalAlchemy.Molecules;

namespace Eigenvalues
{
	public partial class MainForm : Form
	{
		protected Stopwatch clock = new Stopwatch();

		public MainForm()
		{
			InitializeComponent();
			EpsFBox.Text = float.Epsilon.ToString();
		}

		#region - Event hanlders. -
		private void TridiagBtn_Click(object sender, EventArgs e)
		{
			MessageBox.Show("Not implemented.");
			return;

			if (dataTableControl1.DataRows == null)
			{
				MessageBox.Show("Load input data.");
				return;
			}

			var data = MatrixMath.CreateFromStringsList(dataTableControl1.DataRows);
			var res = Numerics.HouseholderTridiag(data);

			var rows = MatrixMath.ConvertToRowsList(res);
			dataTableControl2.DataRows = MatrixMath.ConvertToStringsList(rows);
		}

		private void HausholderBtn_Click(object sender, EventArgs e)
		{
			var data = new float[1, 1];
			if (!GetData(ref data)) return;

			var col = MatrixMath.GetColumn(data, 0);
			var res = Numerics.HouseholderTransform.GetMatrix(VectorMath.Subvector(col, 1, col.Length - 1));

			//var rows = MatrixMath.ConvertToRowsList(res);
			//dataTableControl2.DataRows = MatrixMath.ConvertToStringsList(rows);

			var trHaus = MatrixMath.Mul(res, res);
			var rows = MatrixMath.ConvertToRowsList(trHaus);
			dataTableControl2.DataRows = MatrixMath.ConvertToStringsList(rows);
		}

		private void HessenbergBtn_Click(object sender, EventArgs e)
		{
			var data = new float[1, 1];
			if (!GetData(ref data)) return;
			var res = Numerics.HessenbergTransform.Transform(data);

			var rows = MatrixMath.ConvertToRowsList(res);
			dataTableControl2.DataRows = MatrixMath.ConvertToStringsList(rows);
		}

		private void CheckQ_Click(object sender, EventArgs e)
		{	// checks calculation of the Q matrix for QR-decomposition.
			var data = new float[1, 1];
			if (!GetData(ref data)) return;

			var q = new float[1,1];
			var r = new float[1, 1];
			Numerics.QRDecomposition.GetQR(data, ref q, ref r);
			//var qt = MatrixMath.Transpose(q);
			//q = MatrixMath.Mul(qt, q);

			var rows = MatrixMath.ConvertToRowsList(q);
			dataTableControl2.DataRows = MatrixMath.ConvertToStringsList(rows);
		}

		private void CheckR_Click(object sender, EventArgs e)
		{	// checks calculation of the R matrix for QR-decomposition.
			var data = new float[1, 1];
			if (!GetData(ref data)) return;

			var q = new float[1, 1];
			var r = new float[1, 1];
			Numerics.QRDecomposition.GetQR(data, ref q, ref r);

			var rows = MatrixMath.ConvertToRowsList(r);
			dataTableControl2.DataRows = MatrixMath.ConvertToStringsList(rows);
		}

		private void CheckQR_Click(object sender, EventArgs e)
		{	// checks calculation of the Q and R matrices for QR-decomposition.
			var data = new float[1, 1];
			if (!GetData(ref data)) return;

			var q = new float[1, 1];
			var r = new float[1, 1];
			Numerics.QRDecomposition.GetQR(data, ref q, ref r);
			var a = MatrixMath.Mul(q, r);

			var rows = MatrixMath.ConvertToRowsList(a);
			dataTableControl2.DataRows = MatrixMath.ConvertToStringsList(rows);
		}

		private void CalcUZ_Click(object sender, EventArgs e)
		{
			var data = new float[1, 1];
			if (!GetData(ref data)) return;

			//var dataD = MatrixMath.ConvertToDoubles(data);
			var iter = (int)IterNumeric.Value;
			RegisterStart();
			var ev = Numerics.Eigenvalues.ComputeEigenvalues(data, iter);
			RegisterStop();
			ShowEigenvalues(ev);
		}

		private void CalcHessenbergBtn_Click(object sender, EventArgs e)
		{
			var data = new float[1, 1];
			if (!GetData(ref data)) return;

			//var dataD = MatrixMath.ConvertToDoubles(data);
			var iter = (int)IterNumeric.Value;
			RegisterStart();
			var ev = Numerics.Eigenvalues.EigenvaluesHessenberg(data, iter);
			RegisterStop();
			ShowEigenvalues(ev);
		}

		#region - Methods using MathNet. -
		private void MathNetBtn_Click(object sender, EventArgs e)
		{
			var data = new float[1, 1];
			if (!GetData(ref data)) return;

			var dbl = MatrixMath.ConvertToDoubles(data);
			var m = new Matrix(dbl);
			RegisterStart();
			var ev = m.EigenValues.CopyToArray();
			RegisterStop();
			ShowEigenvalues(ev);
		}

		private void CheckQMathNetBtn_Click(object sender, EventArgs e)
		{
			var data = new float[1, 1];
			if (!GetData(ref data)) return;

			var dbl = MatrixMath.ConvertToDoubles(data);
			var m = new Matrix(dbl);
			var q = m.QRDecomposition.Q;
			var fq = MatrixMath.ConvertToFloats((double[,])q);
			var rows = MatrixMath.ConvertToRowsList(fq);
			dataTableControl2.DataRows = MatrixMath.ConvertToStringsList(rows);
		}

		private void CheckRMathnetBtn_Click(object sender, EventArgs e)
		{
			var data = new float[1, 1];
			if (!GetData(ref data)) return;

			var dbl = MatrixMath.ConvertToDoubles(data);
			var m = new Matrix(dbl);
			var r = m.QRDecomposition.R;
			var fr = MatrixMath.ConvertToFloats((double[,])r);
			var rows = MatrixMath.ConvertToRowsList(fr);
			dataTableControl2.DataRows = MatrixMath.ConvertToStringsList(rows);
		}

		private void HouseMathnetBtn_Click(object sender, EventArgs e)
		{
			var data = new float[1, 1];
			if (!GetData(ref data)) return;

			var dbl = MatrixMath.ConvertToDoubles(data);
			var m = new Matrix(dbl);
			var h = m.QRDecomposition.H;
			var fh = MatrixMath.ConvertToFloats((double[,])h);
			var rows = MatrixMath.ConvertToRowsList(fh);
			dataTableControl2.DataRows = MatrixMath.ConvertToStringsList(rows);
		}
		#endregion
		#endregion

		#region - Utility methods. -
		private void RegisterStart ()
		{
			clock.Reset();
			clock.Start();
		}

		private void RegisterStop()
		{
			clock.Stop();
			TimeBox.Text = string.Format("{0} msec", clock.ElapsedMilliseconds);
		}

		private bool GetData(ref float[,] data)
		{
			if (dataTableControl1.DataRows == null)
			{
				MessageBox.Show("Load input data.");
				return false;
			}

			data = MatrixMath.CreateFromStringsList(dataTableControl1.DataRows);
			return true;
		}

		private void ShowEigenvalues(Complex[] evs)
		{
			var lines = new List<string>();
			foreach (var ev in evs)
			{
				lines.Add(ev.ToString());
			}
			EigenvaluesBox.Lines = lines.ToArray();
		}
		#endregion

		private void RandomMatrixBtn_Click(object sender, EventArgs e)
		{
			// create random symmetric marix.
			var rand = new Random();
			var m = MatrixMath.RandomSymmetric((int)RandomMatrixSizeNumeric.Value, rand);
			var rows = MatrixMath.ConvertToRowsList(m);
			dataTableControl1.DataRows = MatrixMath.ConvertToStringsList(rows);
		}

		private void HessenbergQRBtn_Click(object sender, EventArgs e)
		{
			//
			// test QR step for Hessenberg matrix for a special matrix from Golub et al.
			var h = new [,]{ {3,1,2},{4,2,3},{0,0.01f,1}};
			var res = Numerics.QRDecomposition.HessenbergRQ(h);

			var rows = MatrixMath.ConvertToRowsList(res);
			dataTableControl2.DataRows = MatrixMath.ConvertToStringsList(rows);
		}

		private void GolubHessenbergBtn_Click(object sender, EventArgs e)
		{
			var data = new float[1,1];
			if (!GetData(ref data)) return;

			var iter = (int)IterNumeric.Value;
			RegisterStart();
			var ev = Numerics.Eigenvalues.EigenvaluesHessenbergGolub(data, iter);
			RegisterStop();
			ShowEigenvalues(ev);
		}

		private void GolubHessenbergBtn_Click_1(object sender, EventArgs e)
		{
			var data = new float[1, 1];
			if (!GetData(ref data)) return;

			var res = Numerics.HessenbergTransform.TransformGolub(data);
			var rows = MatrixMath.ConvertToRowsList(res);
			dataTableControl2.DataRows = MatrixMath.ConvertToStringsList(rows);
		}

		private void CheckRQBtn_Click(object sender, EventArgs e)
		{	// checks calculation of the Q and R matrices for QR-decomposition.
			var data = new float[1, 1];
			if (!GetData(ref data)) return;

			var q = new float[1, 1];
			var r = new float[1, 1];
			Numerics.QRDecomposition.GetQR(data, ref q, ref r);
			var a = MatrixMath.Mul(r, q);

			var rows = MatrixMath.ConvertToRowsList(a);
			dataTableControl2.DataRows = MatrixMath.ConvertToStringsList(rows);
		}

		private void RandomAsymMatrixBtn_Click(object sender, EventArgs e)
		{
			// create random marix.
			var rand = new Random();
			var size = (int) RandomMatrixSizeNumeric.Value;
			var m = MatrixMath.Random(size, size, rand);
			var rows = MatrixMath.ConvertToRowsList(m);
			dataTableControl1.DataRows = MatrixMath.ConvertToStringsList(rows);
		}
	}
}
